package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

// 指针强转参考博客 ： https://www.cnblogs.com/fwdqxl/p/7789162.html
type Student struct {
	Name  string
	Age   int
	score float64
}

func test(stu interface{}) {
	stuType := reflect.TypeOf(stu)
	stuValue := reflect.ValueOf(stu)
	if stuType.Kind() == reflect.Ptr { // 传递的是 &stu
		fmt.Println("指针类型")
		pStudType := stuType.Elem()
		fmt.Println(pStudType.Kind()) //struct

		pStuValue := stuValue.Elem()
		fmt.Println(pStuValue.Kind()) //struct

		pvi := pStuValue.Interface()
		fmt.Println(pvi)      //{bart 16 99.9}
		fmt.Printf("%T", pvi) // main.Student
		// 修改值的方式
		// 1) 普通方式
		// sf := pStuValue.FieldByName("Name")
		// old := sf.String()
		// sf.SetString(old + "_new")
		// 2) unsafe.Pointer 方式
		fmt.Println("*************")
		// 2.1）直接使用整个结构体的指针
		// sp := stuValue.Pointer()
		// fmt.Println(sp)           //824633738400
		// fmt.Printf("%T\n", sp)    //uintptr
		// usp := unsafe.Pointer(sp) // 该方式会显示黄色警告、忽略即可
		// ssp := (*Student)(usp)
		// fmt.Println(ssp)  // &{bart 16 99.9}
		// fmt.Println(*ssp) // {bart 16 99.9}
		// oldName := ssp.Name
		// ssp.Name = oldName + "_new"
		// 2.2）针对 field 使用指针
		sf := pStuValue.FieldByName("Name")
		sfp := (*string)(unsafe.Pointer(sf.Addr().Pointer())) // OK，因为使用Addr()返回的就是string的地址
		// sfp := (*string)(unsafe.Pointer(sf.Pointer())) // 报错，因为string本身不是ptr类型
		fmt.Println(sfp)  // 0xc0000044a0
		fmt.Println(*sfp) // bart
		*sfp = *sfp + "_new"

	} else if stuType.Kind() == reflect.Struct { // 传递的是 stu
		fmt.Printf("结构体类型 = %T \n", stuType.Kind()) //结构体类型 = reflect.Kind
		fmt.Println(stuType.Kind())                 // struct
		fmt.Println(stuValue.Kind())                // struct
		fmt.Println(stuValue.Type().Kind())         // struct

		vi := stuValue.Interface()
		fmt.Println(vi)      //{bart 16 99.9}
		fmt.Printf("%T", vi) // main.Student

	} else {
		fmt.Println("未知类型")
	}

}

func main() {
	stu := Student{"bart", 16, 99.9}
	test(&stu)
	fmt.Println(stu) // main.Student{bart_new 16 99.9}
}
